# -*- coding: utf-8 -*-
"""Player operations module for managing player actions."""

from config import Bounds, RuneBooks, GumpIDs
import time

class UOPlayer:
    """A wrapper class for RAE's Player module."""
    
    @staticmethod
    def IsInRange(bounds):
        """Check if the player is within the specified bounds.
        
        Args:
            bounds: A dictionary containing the area boundaries with keys:
                   'min_x', 'max_x', 'min_y', 'max_y', 'min_z'
            
        Returns:
            bool: True if player is within bounds, False otherwise
        """
        # Get player's current position
        x = Player.Position.X
        y = Player.Position.Y
        z = Player.Position.Z
        
        # Check if player is within the bounds
        return (bounds['min_x'] <= x <= bounds['max_x'] and
                bounds['min_y'] <= y <= bounds['max_y'] and
                z == bounds['min_z'])  # Z coordinate must match exactly 
    
    @staticmethod
    def IsBackpackOverLimit(item_count_limit, weight_limit):
        """Check if the player's backpack exceeds item count or weight limits.
        
        Args:
            item_count_limit: Maximum allowed item count ratio (0.0 to 1.0)
            weight_limit: Maximum allowed weight ratio (0.0 to 1.0)
            
        Returns:
            bool: True if backpack exceeds either limit, False otherwise
        """
        # Get backpack contents info
        contents = Items.GetPropValueString(Player.Backpack.Serial, "Contents")
        if not contents:
            return False
            
        # Parse item count from contents string (e.g., "111/125 items, 64/550 stones")
        item_count_parts = contents.split(",")[0].strip().split("/")
        current_items = int(item_count_parts[0])
        max_items = int(item_count_parts[1].split()[0])  # Get only the number part
        
        # Get weight info
        current_weight = Player.Weight
        max_weight = Player.MaxWeight
        
        # Check if either limit is exceeded
        return (current_items / max_items > item_count_limit or 
                current_weight / max_weight > weight_limit)
    
    @staticmethod
    def TeleportUsingRunebook(runebook: 'Item', rune_index: int, timeout_ms: int = 5000):
        """Use a runebook to teleport to a specific rune
    
        Args:
            runebook: The runebook item to use
            rune_index: The index of the rune to teleport to (0-based)
            timeout_ms: Maximum time to wait for teleport in milliseconds (default: 5000ms)
            
        Returns:
            tuple: (success, message)
                - success: True if teleport was successful, False otherwise
                - message: Description of the result or error message
        """
        if not runebook:
            return False, "No runebook provided"
            
        # Check if rune index is valid
        if rune_index < 1 or rune_index > 16:  # Runebooks have 16 rune slots
            return False, f"Invalid rune index: {rune_index}"
            
        # Calculate the action number (5 for first location, 11 for second, 17 for third, etc.)
        # Convert from 1-based to 0-based for internal calculation
        action_number = 5 + ((rune_index - 1) * 6)

        # Store current position for verification
        start_pos = Player.Position

        # Close all open gumps before using the runebook
        while Gumps.CurrentGump() != 0:
            Gumps.CloseGump(Gumps.CurrentGump())
            Misc.Pause(300)  # Wait a bit to ensure the gump is closed

        # Double click runebook
        Items.UseItem(runebook.Serial)
        if not Gumps.WaitForGump(GumpIDs.RUNE_BOOK, 10000):
            return False, "Failed to open runebook gump"

        # Send the action to teleport
        Gumps.SendAction(GumpIDs.RUNE_BOOK, action_number)

        # Wait for teleport to complete with timeout
        start_time = time.time()
        while time.time() - start_time < timeout_ms / 1000:
            # Check if position has changed
            if Player.Position != start_pos:
                Misc.Pause(800) 
                elapsed = time.time() - start_time
                return True, f"Teleport successful in {elapsed:.3f} seconds"
            Misc.Pause(1000)  # Check every 100ms
        
        elapsed = time.time() - start_time
        return False, f"Teleport failed after {elapsed:.3f} seconds"

    @staticmethod
    def MoveToTarget(target, target_distance: int):
        """Move closer to a target (Item or Mobile) until within specified distance
        
        Args:
            target: The target to move towards (Item or Mobile object)
            target_distance: The target distance to reach
            
        Returns:
            tuple: (success, message)
                - success: True if successfully moved within target distance, False otherwise
                - message: Description of the result or error message
        """
        if not target:
            return False, "No target provided"
            
        # Check if already within target distance
        current_distance = Player.DistanceTo(target)
        if current_distance <= target_distance:
            return True, f"Already within target distance (current: {current_distance}, target: {target_distance})"
            
        # Get current positions
        target_x = target.Position.X
        target_y = target.Position.Y
        player_x = Player.Position.X
        player_y = Player.Position.Y
        
        # Keep moving until within target distance
        while Player.DistanceTo(target) > target_distance:
            # Determine direction based on relative positions
            if target_x > player_x and target_y > player_y:
                if not Player.Walk("Southeast"):
                    return False, "Cannot move Southeast - path blocked"
            elif target_x < player_x and target_y > player_y:
                if not Player.Walk("Southwest"):
                    return False, "Cannot move Southwest - path blocked"
            elif target_x > player_x and target_y < player_y:
                if not Player.Walk("Northeast"):
                    return False, "Cannot move Northeast - path blocked"
            elif target_x < player_x and target_y < player_y:
                if not Player.Walk("Northwest"):
                    return False, "Cannot move Northwest - path blocked"
            elif target_x > player_x and target_y == player_y:
                if not Player.Walk("East"):
                    return False, "Cannot move East - path blocked"
            elif target_x < player_x and target_y == player_y:
                if not Player.Walk("West"):
                    return False, "Cannot move West - path blocked"
            elif target_x == player_x and target_y > player_y:
                if not Player.Walk("South"):
                    return False, "Cannot move South - path blocked"
            elif target_x == player_x and target_y < player_y:
                if not Player.Walk("North"):
                    return False, "Cannot move North - path blocked"
                
            # Wait a bit for movement
            Misc.Pause(200)  # 走路速度是每秒5格，所以每步需要200ms
            
            # Update positions
            player_x = Player.Position.X
            player_y = Player.Position.Y
            
        return True, f"Successfully moved within target distance (current: {Player.DistanceTo(target)}, target: {target_distance})"
    
    @staticmethod
    def MoveToPosition(x: int, y: int, z: int, timeout_ms: int = 10000):
        """Move to a specific position using directional movement.
        
        Args:
            x: Target X coordinate
            y: Target Y coordinate
            z: Target Z coordinate
            timeout_ms: Maximum time to wait for movement in milliseconds (default: 10000ms)
            
        Returns:
            tuple: (success, message)
                - success: True if successfully moved to position, False otherwise
                - message: Description of the result or error message
        """
        # Check if already at target position
        if (Player.Position.X == x and 
            Player.Position.Y == y and 
            Player.Position.Z == z):
            return True, "Already at target position"
            
        # Get current positions
        player_x = Player.Position.X
        player_y = Player.Position.Y
        
        # Calculate start time for timeout
        start_time = time.time()
        
        # Keep moving until at target position or timeout
        while (Player.Position.X != x or 
               Player.Position.Y != y or 
               Player.Position.Z != z):
            
            # Check timeout
            if time.time() - start_time > timeout_ms / 1000:
                return False, f"Movement timed out after {timeout_ms/1000} seconds"
            
            # Determine direction based on relative positions
            if x > player_x and y > player_y:
                Player.Walk("Southeast")
            elif x < player_x and y > player_y:
                Player.Walk("Southwest")
            elif x > player_x and y < player_y:
                Player.Walk("Northeast")
            elif x < player_x and y < player_y:
                Player.Walk("Northwest")
            elif x > player_x and y == player_y:
                Player.Walk("East")
            elif x < player_x and y == player_y:
                Player.Walk("West")
            elif x == player_x and y > player_y:
                Player.Walk("South")
            elif x == player_x and y < player_y:
                Player.Walk("North")
                
            # Update current position
            player_x = Player.Position.X
            player_y = Player.Position.Y
            
            # Wait a bit for movement
            Misc.Pause(200)  # 走路速度是每秒5格，所以每步需要200ms
            
        return True, "Successfully moved to target position"

